package com.catazy.base.presentation.ext

import android.content.Context
import android.os.SystemClock
import android.view.KeyEvent
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.withContext

/**
 * 防抖动点击事件
 */
fun View.setOnDebouncedClickListener(action: () -> Unit) {
    val actionDeBouncer = ActionDeBouncer(action)

    // 点击事件
    setOnClickListener {
        actionDeBouncer.notifyAction()
    }
}

/**
 * 移除防抖动点击事件
 */
fun View.removeOnDebouncedClickListener() {
    setOnClickListener(null)
    isClickable = false
}

/**
 * 防抖
 */
private class ActionDeBouncer(private val action: () -> Unit) {

    private var lastActionTime = 0L

    fun notifyAction() {
        val now = SystemClock.elapsedRealtime()

        val millisecondsPassed = now - lastActionTime
        val actionAllowed = millisecondsPassed > DEBOUNCE_INTERVAL_MILLISECONDS
        lastActionTime = now

        if (actionAllowed) {
            action.invoke()
        }
    }

    companion object {
        const val DEBOUNCE_INTERVAL_MILLISECONDS = 600L
    }
}

/**
 * 设置View的visible状态
 */
var View.visible
    get() = visibility == VISIBLE
    set(value) {
        visibility = if (value) VISIBLE else GONE
    }

/**
 * 隐藏
 */
fun View.hide(gone: Boolean = true) {
    visibility = if (gone) GONE else INVISIBLE
}

/**
 * 显示
 */
fun View.show() {
    visibility = VISIBLE
}

/**
 * 搜索框输入监听
 * @param scope 协程作用域
 * @param minimumTextCount 最小输入字符数量
 * @param delayMs 延迟时间
 * @param onQueryTextChanged 输入变化监听
 */
@OptIn(FlowPreview::class)
fun EditText.initSearchBehaviour(
    scope: LifecycleCoroutineScope,
    minimumTextCount: Int,
    delayMs: Long,
    onQueryTextChanged: androidx.appcompat.widget.SearchView.OnQueryTextListener,
) {
    // 在指定的协程作用域（CoroutineScope）中启动一个协程，并且只有在宿主组件（如 Activity 或 Fragment）处于 RESUMED 状态时才会执行该协程
    scope.launchWhenResumed {
        // 创建一个自定义的 Flow
        callbackFlow {
            val watcher = this@initSearchBehaviour.addTextChangedListener {
                // 如果输入字符为null则不做处理
                val query = it?.toString() ?: return@addTextChangedListener
                // 如果输入的字符数大于等于指定的最小数量，则将输入的字符串发送到 Flow 中，否则发送 null
                if (query.count() >= minimumTextCount) {
                    this.trySend(query)
                } else {
                    this.trySend(null)
                }
            }
            // 用于在 callbackFlow 或 channelFlow 中注册一个清理操作
            // 当流被取消或关闭时，awaitClose 块中的代码会被执行，通常用于释放资源或取消订阅
            awaitClose {
                this@initSearchBehaviour.removeTextChangedListener(watcher)
            }
        }
            // 用于过滤流中的数据，确保只有在连续的元素不同时才会发出新的元素。这个操作符通常用于减少不必要的处理和更新，
            // 特别是在处理用户输入或其他频繁变化的数据时
            .distinctUntilChanged()
            // 用于在流中引入防抖机制。防抖机制的作用是，在指定的时间间隔内，只有最后一次事件会被发出。
            // 这在处理用户输入（如搜索框输入）时非常有用，可以避免因频繁的输入而触发过多的处理操作
            .debounce(delayMs)
            // 会取消之前的处理操作并开始新的处理操作
            .collectLatest {
                withContext(scope.coroutineContext) {
                    onQueryTextChanged.onQueryTextChange(it)
                }
            }
    }

    this.setOnEditorActionListener { _, keyCode, keyEvent ->
        return@setOnEditorActionListener if (keyCode == EditorInfo.IME_ACTION_SEARCH
            || keyEvent.keyCode == KeyEvent.KEYCODE_ENTER
        ) {
            onQueryTextChanged.onQueryTextSubmit(this.text?.toString())
            true
        } else {
            false
        }
    }
}

/**
 * 显示软键盘
 */
fun View.showKeyboard(): Boolean {
    this.requestFocus()
    val inputMethodManager: InputMethodManager? =
        context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
    return inputMethodManager?.showSoftInput(this, 0) ?: false
}

/**
 * 隐藏软键盘
 */
fun View.hideKeyboard(): Boolean {
    this.clearFocus()
    val inputMethodManager: InputMethodManager? =
        context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
    return inputMethodManager?.hideSoftInputFromWindow(this.windowToken, 0) ?: false
}